//este es el archivo que habria que reimplementar para hacer que realmente
//vaya a disco, y no que saque las cosas de la memoria

#include "../../include/fileSystem/fileSystem.h"
#include "../../include/fileSystem/directory.h"
#include "../../include/memoryPaging.h"
#include "../../include/kc.h"
#include "../../include/bitmap.h"

#define FILESYSTEM_DIR 0x300000

#define MAX_INODES 1024

//bitmap que indica que inodos estan siendo usados
static bitmap inodeBitmap;

#define MAX_BLOCKS 0x2000
//bitmap que indica que bloques estan siendo usados
static bitmap blockBitmap;

//listado de inodos
static inode_t * inodes;

static int cantInodes;

//inicializacion de la estructura de bloques
static void initBlocks(void);

void
initFileSystem(void) {

	initBitmap(&inodeBitmap, (char*)FILESYSTEM_DIR, MAX_INODES / 8);

	//le digo al blockBitmap que arranque donde termino el otro
	initBitmap(&blockBitmap, inodeBitmap.bpPtr + (MAX_INODES / 8), MAX_BLOCKS / 8);

	//asigno donde arrancan los inodos
	inodes = (inode_t *) (blockBitmap.bpPtr + (MAX_BLOCKS / 8));

	//inicio la estructura de bloques
	initBlocks();

	int inodeNum = 1;
	//voy a asignar el primer inodo al directorio root '/'
	inodes[inodeNum-1].mode = 0 | READ_BIT | WRITE_BIT | DIR_BIT;
	inodes[inodeNum-1].blockNum[0] = allocBlock();
	inodes[inodeNum-1].fileSize = 0;

	//busco el bloque del directorio root
	dirEntry * memDir = (dirEntry *)getBlock(inodes[inodeNum-1].blockNum[0]);

	//agrego las entradas basicas del directorio root
	addDirEntry(memDir, ".", inodeNum, &inodes[inodeNum-1].fileSize, 'd');
	addDirEntry(memDir, "..", inodeNum, &inodes[inodeNum-1].fileSize, 'd');

	writeBlock(inodes[inodeNum-1].blockNum[0], (char *)memDir);
	putBlock(inodes[inodeNum-1].blockNum[0], (char *)memDir);

	//indico que el inodo con numero inodeNum esta ocupado
	turnBitOn(&inodeBitmap, inodeNum-1);

	cantInodes = 1;
}

int
allocInode(inode_t * inode) {
	int pos = findFreePos(&inodeBitmap, 0);

	if (pos != -1) {
		turnBitOn(&inodeBitmap, pos);
		inodes[pos].mode = 0;

		//seteo los bloques en 0
		int i;
		for (i = 1; i < BLOCKS_PER_INODE; i++)
			inodes[pos].blockNum[i] = 0;

		//alloco el primer bloque
		inodes[pos].blockNum[0] = allocBlock();
		//veo que el bloque sea valido
		if (inodes[pos].blockNum[0] != -1) {
			inodes[pos].fileSize = 0;
			memcpy(inode, &inodes[pos],sizeof(inode_t));
			//devuelvo el numero del inodo
			return pos + 1;
		}
		//si el bloque no era valido, desactivo el inodo y devuelvo error
		turnBitOff(&inodeBitmap, pos);
	}

	return -1;
}

int
readInode(int inodeNum, inode_t * inode) {
	//si no esta seteado el bit, devuelvo -1
	if (!isBitOn(&inodeBitmap, inodeNum-1))
		return -1;

	memcpy(inode, &inodes[inodeNum-1],sizeof(inode_t));
	return 1;
}

int
writeInode(int inodeNum, inode_t * inode) {
	//si no esta seteado el bit, devuelvo -1
	if (!isBitOn(&inodeBitmap, inodeNum-1))
		return -1;

	memcpy(&inodes[inodeNum-1], inode, sizeof(inode_t));
	return 1;
}

void
freeInodeByNum(int inodeNum) {
	turnBitOff(&inodeBitmap, inodeNum-1);
	cantInodes--;
}

//funciones de manejo de bloques de disco. al estar todo en memoria, en esta
//implementacion hacen poco o nada. pero en una implementacion sobre un
//disco duro, por ejemplo, harian muchas mas cosas

//esto en una implementacion sobre disco cambiaria drasticamente
typedef struct{
	char * memPtr;		//puntero a zona en memoria
	short int timesOpened;	//lleva cuenta de cuantas veces fue abierto
}block_t;
//listado de bloques
static block_t * blocks;

void
initBlocks(void) {
	blocks = (block_t*)(inodes + MAX_INODES);
}

int
allocBlock() {
	int pos = findFreePos(&blockBitmap, 0);

	if (pos != -1) {
		turnBitOn(&blockBitmap, pos);
		//pongo timesOpened en 0 ya que solo con getBlock aumenta el numero
		blocks[pos].timesOpened = 0;
		blocks[pos].memPtr = pageMalloc(NULL);

		//desactivo la pagina ya que la voy a activar con getBlock
		deactivatePage(blocks[pos].memPtr);

		//si el pageMalloc fallo, desactivo el bloque y devuelvo error
		if (blocks[pos].memPtr == NULL) {
			turnBitOff(&blockBitmap, pos);
			pos = -1;
		}
	}

	//devuelvo el numero de bloque
	return (pos == -1)? pos : pos + 1;
}

char *
getBlock(int blockNum) {
	blockNum--;
	//si no esta seteado el bit, devuelvo NULL
	if (!isBitOn(&blockBitmap, blockNum))
		return NULL;

	if (blocks[blockNum].timesOpened == 0)
		activatePage(blocks[blockNum].memPtr);

	blocks[blockNum].timesOpened++;

	return blocks[blockNum].memPtr;
}

void
putBlock(int blockNum, char * memDir) {
	blockNum--;
	//si no esta seteado el bit, devuelvo salgo
	if (!isBitOn(&blockBitmap, blockNum))
		return;

	//chequeo que esto no baje de 0
	if(blocks[blockNum].timesOpened > 0)
		blocks[blockNum].timesOpened--;

	//si era el ultimo que lo tenia abierto, desactivo la pagina
	if (blocks[blockNum].timesOpened == 0)
		deactivatePage(blocks[blockNum].memPtr);

}

void
writeBlock(int blockNum, char * memDir) {
	//como en realidad memDir siempre apunta a la memoria, y no tengo
	//un lugar en disco a donde copiar lo apuntado por memDir, no hago nada.
	//pero en realidad esta funcion escribiria lo apuntado por memDir a disco.
}

int
freeBlock(int blockNum) {
	blockNum--;
	//si no esta seteado el bit, devuelvo salgo
	if (!isBitOn(&blockBitmap, blockNum))
		return blockNum + 1;

	//apago el bit del bloque, liberandolo
	turnBitOff(&blockBitmap, blockNum);

	pageFree(blocks[blockNum].memPtr);

	return 0;
}

